package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.util.JacksonUtil;
import com.example.lotterysystem.common.util.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataObject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    private final Long WINNING_RECORDS_TIMEOUT = 60*60*24*2L;//单位是s
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_PREFIX_";
    @Autowired
    private RabbitTemplate rabbitTemplate;//包含了RabbitMQ所有支持发送接受的行为
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        //定义一个map作为消息体来进行发送
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID())); //当前消息的id,随机生成一个UUID然后在转换为String
        map.put("messageData", JacksonUtil.writeValueAsString(param));//正文 因为Data设定的是String格式,所有要进行序列化
        //发消息:发送给哪个交换机和队列(交换机和队列是进行了绑定的,需要拿到绑定的key),以及消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);//转换成json格式之后再去进行发送  (交换机,key,对象)
        logger.info("map消息发送成功,map: {}",JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否存在可以直接从 activity_prize 里面查,因为在保存activity时做了本地事务,保证了一致性
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper
                .selectByAPId(param.getActivityId(),param.getPrizeId());

        //活动或者奖品是否存在
        if(null==activityDO||null==activityPrizeDO){
            //活动不存在
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.info("校验抽奖请求失败! 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }
        //活动是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //活动已经完成
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.info("校验抽奖请求失败! 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;
        }

        //奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            //活动已经完成
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY__PRIZE_COMPLETED);
            logger.info("校验抽奖请求失败! 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY__PRIZE_COMPLETED.getMsg());

            return false;
        }

        //中奖者人数是否和设置的奖品数量一致
        if(activityPrizeDO.getPrizeAmount()!= param.getWinnerList().size()){
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY__PRIZE_AMOUNT_ERROR);
            logger.info("校验抽奖请求失败! 失败原因:{}",
                    ServiceErrorCodeConstants.ACTIVITY__PRIZE_AMOUNT_ERROR.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinnerRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关的信息: 活动、人员、奖品、活动关联奖品表...
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userIds = userMapper.batchSelectByIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .toList());
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        //构造中奖者记录,保存(某一活动的某一奖品的中奖列表)
        List<WinnerRecordDO> winnerRecordDOList = userIds.stream()
                .map(userDO -> {
                    WinnerRecordDO winnerRecordDO = new WinnerRecordDO();
                    winnerRecordDO.setActivityId(activityDO.getId());
                    winnerRecordDO.setActivityName(activityDO.getActivityName());
                    winnerRecordDO.setPrizeId(prizeDO.getId());
                    winnerRecordDO.setPrizeName(prizeDO.getName());
                    winnerRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winnerRecordDO.setWinnerId(userDO.getId());
                    winnerRecordDO.setWinnerName(userDO.getUserName());
                    winnerRecordDO.setWinnerEmail(userDO.getEmail());
                    winnerRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winnerRecordDO.setWinningTime(param.getWinningTime());
                    return winnerRecordDO;
                }).toList();
        winningRecordMapper.batchInsert(winnerRecordDOList);

        //缓存中奖者记录
        //1.缓存奖品维度的中奖记录(单个活动的单个奖品)
        cacheWinnerRecords(param.getActivityId()+"_"+param.getPrizeId(),
                winnerRecordDOList,
                WINNING_RECORDS_TIMEOUT);//传的就是key

        //2.缓存活动维度的中奖记录(完整的中奖记录)
        // 当所有奖品都抽完之后,才会对整个活动的中奖记录进行查询
        //当活动完成之后在存放奖品的获奖记录
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //查询活动维度的全量中奖记录
            List<WinnerRecordDO> allList = winningRecordMapper.selecrByActivityId(param.getActivityId());
            cacheWinnerRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winnerRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(null==activityId){
            logger.warn("需要删除的中奖记录的相关活动id不存在");
            return;
        }
        //1.删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);

        //2.删除缓存(奖品维度的缓存+活动维度的缓存)
        if(null!=prizeId){
            //奖品维度的缓存
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //活动维度的缓存
        //无论是否有 prizeId,都需要删除活动维度的缓存
        //没有 prizeId 只能证明当前奖品还没有人中奖,但是不能保证活动其他的奖品还是初始状态(活动初始状态)
        deleteWinningRecords(String.valueOf(activityId));

    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        // 先查询redis
        String key = null==param.getPrizeId()
                ?String.valueOf(param.getActivityId())
                :param.getActivityId()+"_"+ param.prizeId;
        List<WinnerRecordDO> winnerRecordDOS=getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winnerRecordDOS)){
            return convertToWinningRecordDTOList(winnerRecordDOS);
        }
        //没有redis查到就去查库
        winnerRecordDOS = winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());

        //存放记录到redis,方便下一次查询
        if(CollectionUtils.isEmpty(winnerRecordDOS)){
            logger.info("查询的中奖记录为空! param:{}",JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinnerRecords(key,winnerRecordDOS,WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winnerRecordDOS);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinnerRecordDO> winnerRecordDOS) {
        if(CollectionUtils.isEmpty(winnerRecordDOS)){
            return Arrays.asList();
        }
        return winnerRecordDOS
                .stream()
                .map(WinnerRecordDO->{
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(WinnerRecordDO.getWinnerId());
                    winningRecordDTO.setWinnerName(WinnerRecordDO.getWinnerName());
                    winningRecordDTO.setPrizeName(WinnerRecordDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(WinnerRecordDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(WinnerRecordDO.getWinningTime());
                    return winningRecordDTO;
                }).toList();
    }

    /**
     * 从缓存中删除中奖记录
     * @param
     */
    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                //判断里面是否有这个key
                //存在才能删除
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            logger.error("删除中奖记录异常,key:{}",key);
        }
    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winnerRecordDOList
     * @param time
     */
    private void cacheWinnerRecords(String key,
                                    List<WinnerRecordDO> winnerRecordDOList,
                                    Long time) {
        String str = JacksonUtil.writeValueAsString(winnerRecordDOList);
        try {
            if(!StringUtils.hasText(key)
            || CollectionUtils.isEmpty(winnerRecordDOList)){
                logger.warn("需要缓存的部分信息为空! key:{},value:{}",WINNING_RECORDS_PREFIX+key,str);
                return;
            }

            redisUtil.set(WINNING_RECORDS_PREFIX+key,str,time);
        }catch (Exception e){
            logger.error("缓存中奖记录异常!,key:{},value:{}",WINNING_RECORDS_PREFIX+key,str);
        }
    }

    /**
     * 获取redis里面的中奖信息
     * @param key
     */
    private List<WinnerRecordDO> getWinningRecords(String key){
        if(!StringUtils.hasText(key)){
            logger.warn("要从缓存中查询的中奖记录 key 为空,key:{}",key);
            return List.of();
        }
        try {
            String str = redisUtil.get(key);
            if(StringUtils.hasText(str)){
                return List.of();
            }
            return JacksonUtil.readListValue(str,WinnerRecordDO.class);
        }catch (Exception e){
            logger.error("获取redis中的中奖信息失败! key:{}",key);
            return List.of();
        }
    }

}
